
/**
 * 漫画阅读器.
 * @author 砷碲
 */
class SCR {
    /** 图片状态为加载完成 */
    IMG_STATE_SUCCESS = 1;
    /** 图片状态为加载失败 */
    IMG_STATE_ERROR = 0;

    /** 当前元素 */
    el = document.body;
    /** 构建的大体元素列表 */
    els = {};
    /** 阅读类型 */
    readType = "scroll";
    /** 加载类型 */
    loadType = "lazy";
    /** 图片列表 */
    imgs = [];
    /** 当前展示的图片所在图片列表中的位置 */
    imgIndex = 0;
    /** 当前已经处理完的图片位置 */
    loadImgIndex = 0;
    /** 最多同时加载的数量 */
    loadNum = 2;
    /** 滚动元素 */
    scrollEle = window;

    /** 预宽度 */
    preWidth;
    /** 预高度 */
    preHeight;
    /** 预图片 */
    preImg;

    imgSuccess;
    imgError;
    
    //- 懒加载部分
    /** 初始展示图片数量 */
    initNum = 5;
    /** 剩余未读数多少张开始继续加载 */
    offsetNum = 3;

    //- 阅读类型-左右切换
    leftClick;
    rightClick;

    //- 阅读类型-下拉式
    onscroll;

    /**
     * 创建漫画阅读器
     * @param obj 传递的参数对象，具体查阅README文档
     */
    constructor(obj) {
        var that = this;

        if (obj != null) {
            if (obj.el) {
                this.el = typeof obj.el == "string" ? document.querySelector(obj.el) : obj.el;
            }
            if (obj.readType) this.readType = obj.readType;
            if (obj.loadType) this.loadType = obj.loadType;
            if (obj.imgs) this.imgs = obj.imgs;
            if (obj.loadNum) this.loadNum = obj.loadNum;
            if (obj.preWidth) this.preWidth = obj.preWidth;
            if (obj.preHeight) this.preHeight = obj.preHeight;
            if (obj.preImg) this.preImg = obj.preImg;
            if (obj.imgSuccess) this.imgSuccess = obj.imgSuccess;
            if (obj.imgError) this.imgError = obj.imgError;
            if (obj.onImgIndex) this.onImgIndex = obj.onImgIndex;
            
            if (obj.readType == "lr") {
                if (obj.leftClick) this.leftClick = obj.leftClick;
                if (obj.rightClick) this.rightClick = obj.rightClick;
            } else {
                if (obj.scrollEle) this.scrollEle = obj.scrollEle;
            }

            if (obj.loadType == "lazy") {
                if (obj.initNum) this.initNum = obj.initNum;
                if (obj.offsetNum) this.offsetNum = obj.offsetNum;
            }
        }

        let scrDiv = document.createElement("div");
        let scrCDiv = document.createElement("div");
        let imgsDiv = document.createElement("div");

        that.els = {
            scrDiv : scrDiv,
            scrCDiv : scrCDiv,
            imgsDiv : imgsDiv
        };

        scrDiv.className = "scr-container";

        //- 中间部分
        scrCDiv.className = "scr-center";

        //-- 图片列表部分
        imgsDiv.className = "scr-imgs";

        // 根据阅读类型再根据加载类型做不同操作
        if (that.readType == "lr") {
            let leftDiv = document.createElement("div"),
                rightDiv = document.createElement("div");

            leftDiv.className = "scr-imgs-lr-left";
            rightDiv.className = "scr-imgs-lr-right";
            
            leftDiv.onclick = function () {that.prev();};
            rightDiv.onclick = function () {that.next();};
            
            imgsDiv.appendChild(leftDiv);
            imgsDiv.appendChild(rightDiv);
        } else if (that.readType == "scroll") {
        } else { alert("unkonw readType"); }

        // 根据加载类型初始化图片
        if (that.loadType == "full") {
            // 代码复用
            that.append([]);
        } else {
            for (var i = 0; i < this.initNum; i++) {
                that.load(i);
            }
        }
        
        // 显示第一张图片，左右切换模式专用，执行不影响其他模式
        if (that.imgs.length > 0) that.imgs[0].el.style.display = "block";

        //-- 图片列表部分结尾

        scrCDiv.appendChild(imgsDiv);
        //- 中间部分结尾

        scrDiv.appendChild(scrCDiv);
        this.el.appendChild(scrDiv);

        //- 事件
        if (that.readType == "scroll") {
            //--滚动元素滚动事件
            var tmpScroll = this.scrollEle.onscroll;
            let upScrollTop = 0;
            let upScrollTime = 0;

            this.scrollEle.onscroll = that.onscroll ? that.onscroll : function (e) {
                let scrollTop = that.getScrollTop();

                that.getImgIndex();

                // 300毫秒触发间隔
                let curTime = new Date().getTime();
                if (curTime - upScrollTime > 300) {
                    upScrollTime = curTime;
    
                    // 向下滚动，有懒加载则判定
                    if (scrollTop - upScrollTop > 0) {
                        that.lazyLoad();
                    }
                }
    
                upScrollTop = scrollTop;
                if (tmpScroll) tmpScroll();
            };

            this.scrollEle.onscroll();
        }

    }

    /** 获取当前滚动元素的顶部滚动位置 */
    getScrollTop() {
        return this.scrollEle == window ? window.scrollY : this.scrollEle.scrollTop;
    }

    /** 滚动位置的最大值 */
    getScrollMaxTop() {
        return this.scrollEle == window
            ? document.documentElement.scrollHeight - document.documentElement.clientHeight
            : this.scrollEle.scrollHeight - this.scrollEle.clientHeight;
    }

    /** 获取阅读器在滚动条的初始偏移（顶部可能有其他元素占据高度） */
    getOffsetTop() {
        var that = this;

        let offsetTop = 0;
        let tmpNode = that.els.scrDiv;
        
        if (that.scrollEle == window) {
            offsetTop = tmpNode.offsetTop;
        } else {
            while (tmpNode != null) {
                var childs = tmpNode.parentNode.children;
                for (var i = 0; i < childs.length; i++) {
                    var child = childs[i];
                    if (child == tmpNode) break;
                    
                    // 高度包括 margin，padding，border
                    var style = window.getComputedStyle(child);

                    offsetTop += child.clientHeight
                        + parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)
                        + parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)
                        + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
                }

                tmpNode = tmpNode.parentNode == that.scrollEle ? null : tmpNode.parentNode;
            }
        }

        return offsetTop;
    }

    /** 获取图片在 imgs 中的真实位置 */
    getImgIndex() {
        var that = this;

        if (that.imgs.length == 0) return 0;

        let offsetTop = that.getOffsetTop();
        let top = that.getScrollTop() - offsetTop;
        let imgIndex;
        
        // 第一张与最后一张直接判断，其余用中间定位
        if (top <= that.imgs[0].el.clientHeight) {
            imgIndex = 0;
        } else if (top + offsetTop >= that.getScrollMaxTop() - that.imgs[that.loadImgIndex - 1].el.clientHeight) {
            imgIndex = that.loadImgIndex - 1;
        } else {
            var total = 0;
            for (var i = 0; i < that.imgs.length; i++) {
                total += that.imgs[i].el.clientHeight;
                if (top <= total) {
                    imgIndex = i;
                    break;
                }
            }
        }
        
        // 较之前有改变则触发
        if (that.imgIndex != imgIndex) {
            that.imgIndex = imgIndex;
            if (that.onImgIndex) that.onImgIndex();
        }

        return imgIndex;
    }

    /**
     * 加载属性 `imgs` 指定位置的元素并添加到页面末尾。
     * @param index 元素在imgs中的位置
     * @param onload 加载完成时触发
     * @param onerror 加载失败时触发
     * @return 加载在文档的元素
     */
    load(index, onload, onerror) {
        var that = this;

        let img = that.imgs[index];
        if (img == null) return;
        that.loadImgIndex++;
        
        let imgEle = document.createElement("img");
        img.el = imgEle;

        // 左右切换模式图片隐藏
        if (that.readType == "lr") imgEle.style.display = "none";

        // 预加载设置
        if (img.preWidth) imgEle.style.width = img.preWidth;
        else if (that.preWidth) imgEle.style.width = that.preWidth;

        if (img.preHeight) imgEle.style.height = img.preHeight;
        else if (that.preHeight) imgEle.style.height = that.preHeight;
        
        imgEle.src = that.preImg ? that.preImg : img.url;

        // 检测是否预加载图片的封装，返回boolean
        function isPre(el) {
            img.el = el;
            if (that.preImg != null && el.src == that.preImg) {
                el.style.width = img.width ? img.width : "100%";
                el.style.height = img.height ? img.height : "auto";
                el.src = img.url;
                return true;
            }
            return false;
        }

        imgEle.onload = function (e) {
            if (!isPre(this)) {
                img.state = that.IMG_STATE_SUCCESS;

                if (that.imgSuccess) that.imgSuccess(img, index, e);
                if (onload) onload(e);
            }
        };
        imgEle.onerror = function (e) {
            if (!isPre(this)) {
                img.state = that.IMG_STATE_ERROR;

                if (that.imgError) that.imgError(img, index, e);
                if (onerror) onerror(e);
            }
        };

        return that.els.imgsDiv.appendChild(imgEle);
    }

    /**
     * 懒加载封装，当加载类型以及条件满足时进行加载操作。
     */
    lazyLoad() {
        if (this.loadType == "lazy" && this.loadImgIndex - this.imgIndex <= this.offsetNum) {
            for (var i = 0; i < this.loadNum; i++) {
                this.load(this.loadImgIndex);
            }
        }
    }

    /**
     * 上一页
     */
    prev() {
        var that = this;
        if (that.imgIndex == 0) return;

        if (that.readType == "lr") {
            if (that.leftClick == null || that.leftClick() != true) {
                // 当前图片隐藏，显示上一张
                that.imgs[that.imgIndex--].el.style.display = "none";
                that.imgs[that.imgIndex].el.style.display = "block";
            }
        } else if (that.readType == "scroll") {
            // 移动到指定位置
            var total = 0;
            for (var i = 0; i < that.imgIndex - 1; i++) {
                total += that.imgs[i].el.clientHeight;
            }

            that.scrollEle.scrollTo(0, Math.ceil(total + that.getOffsetTop()));
        }
    }

    /**
     * 下一页
     */
    next() {
        var that = this;
        if (that.imgIndex >= that.imgs.length - 1) return;

        if (that.readType == "lr") {
            if (that.leftClick == null || that.leftClick() != true) {
                // 当前图片隐藏，显示下一张，懒加载则判定是否继续加载
                that.imgIndex++;

                that.lazyLoad();

                that.imgs[that.imgIndex-1].el.style.display = "none";
                that.imgs[that.imgIndex].el.style.display = "block";
            }
        } else if (that.readType == "scroll") {
            // 移动到指定位置
            var total = 0;
            for (var i = 0; i <= that.imgIndex; i++) {
                total += that.imgs[i].el.clientHeight;
            }

            that.scrollEle.scrollTo(0, Math.ceil(total + that.getOffsetTop()));
        }
    }

    /**
     * 指定页
     * @param index 
     */
    to(index) {
        var that = this;
        if (index < 0 || index >= that.imgs.length) return;

        if (that.readType == "lr") {
            that.imgs[that.imgIndex].el.style.display = "none";
            that.imgs[index].el.style.display = "block";
            that.imgIndex = index;
        } else if (that.readType == "scroll") {
            var total = 0;
            for (var i = 0; i < index; i++) {
                total += that.imgs[i].el.clientHeight;
            }

            that.scrollEle.scrollTo(0, Math.ceil(total + that.getOffsetTop()));
        }
    }

    /**
     * 追加图片列表，用于连续章节等。
     */
    append(imgs) {
        var that = this;

        // 元素加入imgs
        for (var i = 0; i < imgs.length; i++) {
            that.imgs.push(imgs[i]);
        }

        if (that.loadType == "full") {
            function recursion() {
                that.load(that.loadImgIndex, function () {
                    recursion();
                }, function () {
                    recursion();
                });
            }

            for (var i = 0; i < that.loadNum; i++) {
                recursion();
            }
        } else {
            that.lazyLoad();
        }
    }

}

// css
var style = document.createElement("style");
style.textContent = `.scr-container,.scr-container>*{position:relative;background:#eee}.scr-center{position:relative;width:100%;display:flex;flex-direction:column;align-items:center}.scr-imgs{position:relative;width:100%}.scr-imgs>img{width:100%;height:auto;display:block}.scr-imgs-lr-left{position:absolute;width:50%;left:0;top:0;height:100%}.scr-imgs-lr-right{position:absolute;width:50%;right:0;top:0;height:100%}@media(min-width:768px){.scr-imgs{max-width:768px}}`;
document.head.append(style);

// 暴露对象到全局
if (typeof window !== 'undefined') {
    window.SCR = SCR;
}

export default SCR;